/*
   This file is part of go-palletone.
   go-palletone is free software: you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation, either version 3 of the License, or
   (at your option) any later version.
   go-palletone is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.
   You should have received a copy of the GNU General Public License
   along with go-palletone.  If not, see <http://www.gnu.org/licenses/>.
*/
/*
 * @author PalletOne core developer Albert·Gou <dev@pallet.one>
 * @date 2018
 */

package mediatorplugin

import (
	"encoding/json"
	"os"

	"github.com/palletone/go-palletone/common"
	"github.com/palletone/go-palletone/common/log"
	"github.com/palletone/go-palletone/core"
	"go.dedis.ch/kyber/v3"
	"gopkg.in/urfave/cli.v1"
)

const (
	DefaultPassword              = "1"
	DefaultInitPrivKey           = "3ouzX3aTwrJM6PaqJrvE9XEWiycKuFgnZcfrW7duMEFu"
	DefaultRequiredParticipation = 33
)

var (
	MediatorFlags = []cli.Flag{
		NoProduceUnitFlag,
		StaleProductionFlag,
		ConsecutiveProductionFlag,
		RequiredParticipationFlag,
		NoGroupSignFlag,
		IdledProducingFlag,
		MediatorsFlag,
	}

	NoProduceUnitFlag = cli.BoolFlag{
		Name:  "noProduce",
		Usage: "Disable producing unit when start up node.",
	}
	StaleProductionFlag = cli.BoolFlag{
		Name:  "staleProduce",
		Usage: "Enable unit production(only once), even if the chain is stale.",
	}
	ConsecutiveProductionFlag = cli.BoolFlag{
		Name:  "allowConsecutive",
		Usage: "Enable unit production(only once), even if the last unit was generated by the same mediator.",
	}
	RequiredParticipationFlag = cli.UintFlag{
		Name:  "requiredParticipation",
		Usage: "Percent of mediators (0-99) that must be participating in order to produce units.",
		//Value: DefaultRequiredParticipation,
	}
	NoGroupSignFlag = cli.BoolFlag{
		Name:  "noGroupSign",
		Usage: "Disable group-signing in this node.",
	}
	MediatorsFlag = cli.StringSliceFlag{
		Name: "mediators",
		Usage: "the mediator account controlled by this node, may specify multiple times. for example:\n" +
			"{\\\"Address\\\":\\\"P1xx\\\",\\\"Password\\\":\\\"xxx\\\",\\\"InitPrivKey\\\":\\\"xxx\\\"," +
			"\\\"InitPubKey\\\":\\\"xxx\\\"}",
	}
	IdledProducingFlag = cli.BoolFlag{
		Name:  "idledProducing",
		Usage: "Enable producing unit, even if we have no pending transactions.",
	}
)

// config data for mediator plugin
type Config struct {
	// the set of mediator accounts controlled by this node
	Mediators []*MediatorConf

	// Percent of mediators (0-99) that must be participating in order to produce uints
	RequiredParticipation uint32

	// 主程序启动时，是否立即开启unit生产
	EnableProducing bool

	// Enable Unit production(only once), even if the chain is stale. 运行本节点开始生产unit，即使数据不是最新的
	EnableStaleProduction bool

	// Enable Unit production(only once), even if the last unit was generated by the same mediator.
	// 允许本节点的mediator可以连续生产unit
	EnableConsecutiveProduction bool

	// 标记本节点是否开启群签名的功能
	EnableGroupSigning bool

	// 标记本节点是否生产无交易的区块
	EnableIdledProduction bool
}

func DefaultMediatorConf() *MediatorConf {
	return &MediatorConf{
		core.DefaultMediator,
		DefaultPassword,
		DefaultInitPrivKey,
		core.DefaultInitPubKey,
	}
}

// mediator plugin default config
var DefaultConfig = Config{
	EnableProducing:             true,
	EnableStaleProduction:       false,
	EnableConsecutiveProduction: false,
	RequiredParticipation:       DefaultRequiredParticipation,
	EnableGroupSigning:          true,
	EnableIdledProduction:       true,
	Mediators: []*MediatorConf{
		DefaultMediatorConf(),
	},
}

func MakeConfig() Config {
	cfg := DefaultConfig
	cfg.Mediators = nil
	return cfg
}

func SetMediatorConfig(ctx *cli.Context, cfg *Config) {
	if ctx.GlobalIsSet(NoProduceUnitFlag.Name) {
		cfg.EnableProducing = false
	}

	if ctx.GlobalIsSet(StaleProductionFlag.Name) {
		cfg.EnableStaleProduction = true
	}

	if ctx.GlobalIsSet(ConsecutiveProductionFlag.Name) {
		cfg.EnableConsecutiveProduction = true
	}

	if ctx.GlobalIsSet(RequiredParticipationFlag.Name) {
		cfg.RequiredParticipation = uint32(ctx.GlobalUint(RequiredParticipationFlag.Name))
	}

	if ctx.GlobalIsSet(NoGroupSignFlag.Name) {
		cfg.EnableGroupSigning = false
	}

	if ctx.GlobalIsSet(IdledProducingFlag.Name) {
		cfg.EnableIdledProduction = true
	}

	if ctx.GlobalIsSet(MediatorsFlag.Name) {
		mjs := ctx.GlobalStringSlice(MediatorsFlag.Name)
		//log.Debugf("%v", mjs)

		for _, mj := range mjs {
			//log.Debugf("%v", mj)
			mc := new(MediatorConf)

			err := json.Unmarshal([]byte(mj), mc)
			if err != nil {
				log.Errorf("%v", err)
				os.Exit(1)
			}

			//log.Debugf("%v", mc)
			cfg.Mediators = append(cfg.Mediators, mc)
		}
	}
}

type MediatorConf struct {
	Address     string //`json:"account" toml:"account"`
	Password    string //`json:"password" toml:"password"`
	InitPrivKey string //`json:"initPrivKey" toml:"initPrivKey"`
	InitPubKey  string //`json:"initPubKey" toml:"initPubKey"`
}

func (medConf *MediatorConf) configToAccount() *MediatorAccount {
	// 1. 解析 mediator 账户地址
	addr, err := core.StrToMedAdd(medConf.Address)
	if err != nil {
		log.Debugf(err.Error())
		return nil
	}

	// 2. 解析 mediator 的 DKS 初始公私钥
	sec, err := core.StrToScalar(medConf.InitPrivKey)
	if err != nil {
		log.Debugf(err.Error())
		return nil
	}

	pub, err := core.StrToPoint(medConf.InitPubKey)
	if err != nil {
		log.Debugf(err.Error())
		return nil
	}

	medAcc := &MediatorAccount{
		addr,
		medConf.Password,
		sec,
		pub,
	}

	return medAcc
}

type MediatorAccount struct {
	Address     common.Address
	Password    string
	InitPrivKey kyber.Scalar
	InitPubKey  kyber.Point
}
